/*
 *    Copyright 2008 Tim Jansen
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.actorsguildframework;

import java.util.concurrent.Callable;

import org.actorsguildframework.annotations.Initializer;
import org.actorsguildframework.annotations.Message;
import org.actorsguildframework.annotations.Model;
import org.actorsguildframework.annotations.Usage;
import org.actorsguildframework.internal.ActorProxy;
import org.actorsguildframework.internal.ActorState;
import org.actorsguildframework.internal.ImmediateExceptionResult;
import org.actorsguildframework.internal.MessageCaller;
import org.actorsguildframework.internal.MessageInvocation;
import org.actorsguildframework.internal.ThreadState;

/**
 * Actor is the class that all actors need to implement.
 * <p>
 * An actor should not define a constructor, but instead define an {@link Initializer}. 
 * The framework will create new instances of the actor using default constructor 
 * (with arguments), so it is possible to create one. However, doing this is not recommended
 * and you have to be careful to get it right. 
 * <p>
 * You can not create an actor using the 'new' operator. Doing so would throw an exception. 
 * Instead {@link Agent#create(Class)} must be used to create a new instance. Agent returns a 
 * proxy for the actual Actor which must be used to access the actor. Most of the features of the 
 * Actor's Guild framework are being done by this invisible proxy.  
 * <p>
 * An actor can have one or more {@link Initializer}s. If the actor has an initializer,
 * it must be called before any message can be sent to the actor. For any attempt to send a 
 * regular message before the initializer, the proxy will throw a 
 * {@link ActorRuntimeException}. 
 * <p>
 * If an actor does not have any initializers, it can be used immediately.
 * <p>
 * Every actor should define one or more methods to receive messages. These methods must be
 * annotated with {@link Message}, and follow the rules given in the documentation of @Message. 
 * All the actual work is being done by the message implementations. Initializers are just a special
 * kind of method.
 * <p>
 * By default, an actor receives only one message at a time. Thus is it operated single-threaded,
 * and does not need to take care of such things as synchronization. In some cases, especially
 * to integrate external interfaces and code, it is useful or even required for an actor to
 * process several messages simultaneously.  This can be declared using the {@link Model}
 * and {@link Usage} annotation. You can also use {@link #lock(Runnable)} to have single-threaded
 * sections in a multi-threaded message implementation.
 * <p>
 * An actor can have public methods that are neither @Initializer nor @Message. This can be used
 * for properties that configure the Actor, and allows the integration of Actors into frameworks
 * like Spring. However, you are responsible for synchronization of such methods. It is recommended
 * to either use 'volatile' fields for properties, or to synchronize on the instance while
 * accessing fields. Initializers and messages will also synchronize on the instance while running.
 * @see Agent
 * @see Agent#create(Class)
 */
public abstract class Actor {
	
	/**
	 * The default constructor for all Actors. It will check whether the new Actor sub-class
	 * is a proxy. If it is not a proxy, an exception will be thrown to prevent this.
	 * You must create all Actors using the methods {@link DefaultAgent#create(Class)}. 
	 * @throws ActorRuntimeException if the instantiated actor is not a proxy class
	 */
	public Actor() {
		if (!(this instanceof ActorProxy))
			throw new ActorRuntimeException("You must not use an Actor's constructor directly, because "+
					"then the would not have its proxy. Use Agent.createActor() to create instances "+
					"of Actors instead.");
	}
	
	/**
	 * Returns the Agent of this Actor.
	 * @return the Actor's Agent
	 * @see Actor#getCurrentAgent()
	 */
	public final Agent getAgent() {
		return ActorState.getState(this).getController().getAgent();
	}
	
	/**
	 * Helper method to create a new ImmediateResult instance.
	 * @param <T> the type of result
	 * @param value the result value
	 * @return the ImmediateResult instance
	 */
	public <T> ImmediateResult<T> result(T value) {
		return new ImmediateResult<T>(value);
	}
	
	/**
	 * Helper method to create a new ImmediateResult instance for methods that have
	 * no result.
	 * @return the ImmediateResult instance (carries null as value)
	 */
	public ImmediateResult<Void> noResult() {
		return new ImmediateResult<Void>(null);
	}
	
	/**
	 * Executes the given Runnable with locking. This method usually only makes sense in
	 * multi-threaded message implementations, and will cause the Runnable to execute
	 * as if it was executed in a single-threaded message. Thus it will never run at the same
	 * time as another locked Runnable or a single-threaded message. The framework will
	 * also synchronize with 'this' during the invocation.
	 * <p>
	 * Using lock (or {@link #lock(Callable)}) is useful when you have sections of single-threaded 
	 * code mixed with code that needs to wait for a long-running message. If a single-threaded message implementation
	 * would wait for such a message, the whole Actor will be blocked. By using lock() only 
	 * for parts of the implementation, you can increase the throughput of your Actor. Just
	 * lock during the parts that modify the Actor's members, but send the message
	 * without being locked.
	 * <p>
	 * It is allowed to use lock() in a single-threaded message. It will not have any effect then.
	 * You must not execute any long-running, blocking thread in the message.
	 *  
	 * @param runnable the runnable to execute.
	 * @see #lock(Callable)
	 */
	protected final void lock(final Runnable runnable) {
		final Object sync = this;
		MessageCaller<Void> mc = new MessageCaller<Void>() {
			@Override
			public AsyncResult<Void> invoke(Actor instance, Object[] arguments) {
				synchronized (sync) {
					try {
						runnable.run();
					}
					catch (Exception e) {
						return new ImmediateExceptionResult<Void>(e);
					}
				}
				return new ImmediateResult<Void>(null);
			}
			
			@Override
			public String getMessageName() {
				return "(internal lock message)";
			}
		};
		lock(mc);
	}
	
	/**
	 * Executes the given Callable with locking and returns its result. This method usually only 
	 * makes sense in multi-threaded message implementations, and will cause the Callable to execute
	 * as if it was executed in a single-threaded message. Thus it will never run at the same
	 * time as another locked Runnable or a single-threaded message. The framework will
	 * also synchronize with 'this' during the invocation.
	 * <p>
	 * Using lock (or {@link #lock(Runnable)}) is useful when you have sections of single-threaded 
	 * code mixed with code that needs to wait for a long-running message. If a single-threaded message implementation
	 * would wait for such a message, the whole Actor will be blocked. By using lock() only 
	 * for parts of the implementation, you can increase the throughput of your Actor. Just
	 * lock during the parts that modify the Actor's members, but send the message
	 * without being locked.
	 * <p>
	 * It is allowed to use lock() in a single-threaded message. It will not have any effect then.
	 * You must not execute any long-running, blocking thread in the message.
	 * 
	 * @param <T> the return type of Callable
	 * @param callable the callable to execute
	 * @return the return value of the callable
	 * @throws WrappedException if the Callable threw an Exception
	 * @see #lock(Runnable)
	 */
	protected final <T> T lock(final Callable<T> callable) throws WrappedException {
		final Object sync = this;
		MessageCaller<T> mc = new MessageCaller<T>() {
			@Override
			public AsyncResult<T> invoke(Actor instance, Object[] arguments){
				synchronized (sync) {
					try {
						return new ImmediateResult<T>(callable.call());
					}
					catch (Exception e) {
						return new ImmediateExceptionResult<T>(e);
					}
				}
			}
			
			@Override
			public String getMessageName() {
				return "(internal lock message)";
			}
		};
		return lock(mc);
	}

	/**
	 * Internal lock implementation
	 * @param <T> the result type
	 * @param mc the MessageCaller
	 * @return the result
	 */
	@SuppressWarnings("unchecked")
	private <T> T lock(MessageCaller<T> mc) {
		ActorState as = ActorState.getState(this);
		ThreadState ts = ThreadState.get();
		MessageInvocation<?> mi = ts.getCurrentInvocation();
		
		if ((mi != null) && (mi.getTargetActor() == as) && !mi.getConcurrencyModel().isMultiThreadingCapable())
			return mc.invoke(this, null).get();
		else
			return (T) as.executeMessageInThread(ts, mc).get();
	}

	/**
	 * Returns the current thread's agent, if the current thread is a thread processing an actor message.
	 * Otherwise it returns null.
	 * @return the current agent, or null
	 * @see Actor#getAgent()
	 */
	public static Agent getCurrentAgent() {
		MessageInvocation<?> mi = ThreadState.get().getCurrentInvocation();
		if (mi == null)
			return null;
		return mi.getTargetActor().getController().getAgent();
	}
	
}
